package com.nageoffer.shortlink.project.service.Impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.nageoffer.shortlink.project.dao.entity.*;
import com.nageoffer.shortlink.project.dao.mapper.LinkAccessLogsMapper;
import com.nageoffer.shortlink.project.dao.mapper.LinkAccessStatsMapper;
import com.nageoffer.shortlink.project.dao.mapper.LinkBrowserStatsMapper;
import com.nageoffer.shortlink.project.dao.mapper.LinkDeviceStatsMapper;
import com.nageoffer.shortlink.project.dao.mapper.LinkLocaleStatsMapper;
import com.nageoffer.shortlink.project.dao.mapper.LinkNetworkStatsMapper;
import com.nageoffer.shortlink.project.dao.mapper.LinkOsStatsMapper;
import com.nageoffer.shortlink.project.dto.req.ShortLinkAccessRecordReqDTO;
import com.nageoffer.shortlink.project.dto.req.ShortLinkStatsReqDTO;
import com.nageoffer.shortlink.project.dto.resp.*;
import com.nageoffer.shortlink.project.service.ShortLinkStatsService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.sql.Wrapper;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 短链接监控接口实现层
 */
@Service
@RequiredArgsConstructor
public class ShortLinkStatsServiceImpl implements ShortLinkStatsService {

    private final LinkAccessStatsMapper linkAccessStatsMapper;
    private final LinkLocaleStatsMapper linkLocaleStatsMapper;
    private final LinkAccessLogsMapper linkAccessLogsMapper;
    private final LinkBrowserStatsMapper linkBrowserStatsMapper;
    private final LinkOsStatsMapper linkOsStatsMapper;
    private final LinkDeviceStatsMapper linkDeviceStatsMapper;
    private final LinkNetworkStatsMapper linkNetworkStatsMapper;

    /**
     * 监控单个短链接
     * @param requestParam
     * @return
     */
    @Override
    public ShortLinkStatsRespDTO oneShortLinkStats(ShortLinkStatsReqDTO requestParam) {
        //基础访问
        List<LinkAccessStatsDo> listStatsByShortLink = linkAccessStatsMapper.listStatsByShortLink(requestParam);
        //地区访问
        List<ShortLinkStatsLocaleCNRespDTO> localeCNRespDTOS =new ArrayList<>();
        List<LinkLocaleStatsDo> linkLocaleStatsDos = linkLocaleStatsMapper.listLocaleByShortLink(requestParam);
        int localCntSum = linkLocaleStatsDos.stream()
                .mapToInt(LinkLocaleStatsDo::getCnt)
                .sum();
        linkLocaleStatsDos.forEach(each ->{
            double ratio = (double) each.getCnt() /localCntSum;
            double actualRatio = Math.round(ratio*100)/100.0;
            ShortLinkStatsLocaleCNRespDTO localeCNRespDTO = ShortLinkStatsLocaleCNRespDTO.builder()
                    .cnt(each.getCnt())
                    .locale(each.getProvince())
                    .ratio(actualRatio)
                    .build();
            localeCNRespDTOS.add(localeCNRespDTO);
        });
        //小时访问详细
        List<Integer> hourStats = new ArrayList<>();
        List<LinkAccessStatsDo> listHourStatsByShortLink = linkAccessStatsMapper.listHourStatsByShortLink(requestParam);
        for (int i = 0;i<24;i++){
            AtomicInteger hour = new AtomicInteger(i);
            int hourCnt = listHourStatsByShortLink.stream()
                    .filter(each->Objects.equals(each.getHour(),hour.get()))
                    .findFirst()
                    .map(LinkAccessStatsDo::getPv)
                    .orElse(0);
            hourStats.add(hourCnt);
        }
        //高频ip访问详细
        List<ShortLinkStatsTopIpRespDTO> topIpStats = new ArrayList<>();
        List<HashMap<String,Object>> listTopIpByShortLink = linkAccessLogsMapper.listTopIpByShortLink(requestParam);
        listTopIpByShortLink.forEach(each->{
            ShortLinkStatsTopIpRespDTO shortLinkStatsTopIpRespDTO = ShortLinkStatsTopIpRespDTO.builder()
                    .ip(each.get("ip").toString())
                    .cnt(Integer.parseInt(each.get("count").toString()))
                    .build();
            topIpStats.add(shortLinkStatsTopIpRespDTO);
        });
        //一周访问详细
        List<Integer> weekdatStats = new ArrayList<>();
        List<LinkAccessStatsDo> linkWeekdayStatsByShortLink = linkAccessStatsMapper.listWeekdayStatsByShortLink(requestParam);
        for (int i =0;i<8;i++){
            AtomicInteger weekday = new AtomicInteger(i);
            int weekdayCnt = linkWeekdayStatsByShortLink.stream()
                    .filter(each->Objects.equals(each.getWeekday(),weekday.get()))
                    .findFirst()
                    .map(LinkAccessStatsDo::getPv)
                    .orElse(0);
            weekdatStats.add(weekdayCnt);
        }
        //浏览器访问详细
        List<ShortLinkStatsBrowserRespDTO> browserRespDTOList = new ArrayList<>();
        List<HashMap<String,Object>> browserHash = linkBrowserStatsMapper.listBrowserStatsByShortLink(requestParam);
        int browserCntSum = browserHash.stream()
                .mapToInt(each->Integer.parseInt(each.get("count").toString()))
                .sum();
        browserHash.forEach(each->{
            double ratio = (double) Integer.parseInt(each.get("count").toString())/browserCntSum;
            double actualRatio = Math.round(ratio*100)/100.0;
            ShortLinkStatsBrowserRespDTO shortLinkStatsBrowserRespDTO = ShortLinkStatsBrowserRespDTO.builder()
                    .cnt(Integer.parseInt(each.get("count").toString()))
                    .browser(each.get("browser").toString())
                    .ratio(actualRatio)
                    .build();
            browserRespDTOList.add(shortLinkStatsBrowserRespDTO);
        });
        //操作系统统计
        List<ShortLinkStatsOsRespDTO> osRespDTOS = new ArrayList<>();
        List<HashMap<String,Object>> osHash = linkOsStatsMapper.listOsStatsByShortLink(requestParam);
        int osSum = osHash.stream()
                .mapToInt(each->Integer.parseInt(each.get("count").toString()))
                .sum();
        osHash.forEach(each->{
            double ratio = (double) Integer.parseInt(each.get("count").toString())/osSum;
            double actualRatio = Math.round(ratio*100)/100.0;
            ShortLinkStatsOsRespDTO shortLinkStatsOsRespDTO = ShortLinkStatsOsRespDTO
                    .builder()
                    .os(each.get("os").toString())
                    .cnt(Integer.parseInt(each.get("count").toString()))
                    .ratio(actualRatio)
                    .build();
        });
        //访客类型详细
        List<ShortLinkStatsUvRespDTO> shortLinkStatsUvRespDTOS = new ArrayList<>();
        HashMap<String,Object> findUvTypeaByShortLink = linkAccessLogsMapper.findUvTypeCntByShortLink(requestParam);
        int oldUserCnt = Integer.parseInt(findUvTypeaByShortLink.get("oldUserCnt").toString());
        int newUserCnt = Integer.parseInt(findUvTypeaByShortLink.get("newUserCnt").toString());
        int userCntSum = oldUserCnt+newUserCnt;
        double oldRatio = (double)  oldUserCnt/userCntSum;
        double actualOldRatio = Math.round(oldRatio*100)/100.0;
        double newRatio = (double) newUserCnt/userCntSum;
        double actualNewRatio = Math.round(newRatio*100)/100.0;
        ShortLinkStatsUvRespDTO oldUser = ShortLinkStatsUvRespDTO.builder()
                .uvType("oldUser")
                .cnt(oldUserCnt)
                .ratio(actualOldRatio)
                .build();
        shortLinkStatsUvRespDTOS.add(oldUser);
        ShortLinkStatsUvRespDTO newUser = ShortLinkStatsUvRespDTO.builder()
                .cnt(newUserCnt)
                .ratio(actualNewRatio)
                .uvType("newUser")
                .build();
        shortLinkStatsUvRespDTOS.add(newUser);
        //访问设备类型详细
        List<ShortLinkStatsDeviceRespDTO> deviceRespDTOS = new ArrayList<>();
        List<LinkDeviceStatsDO> deviceStatsDOS = linkDeviceStatsMapper.listDeviceStatsByShortLink(requestParam);
        int deviceSum = deviceStatsDOS.stream()
                .mapToInt(LinkDeviceStatsDO::getCnt)
                .sum();
        deviceStatsDOS.forEach(each->{
            double ratio = (double) each.getCnt()/deviceSum;
            double actualRatio = Math.round(ratio*100)/100.0;
            ShortLinkStatsDeviceRespDTO shortLinkDeviceRespDTO  = ShortLinkStatsDeviceRespDTO.builder()
                    .cnt(each.getCnt())
                    .device(each.getDevice())
                    .ratio(actualRatio)
                    .build();
            deviceRespDTOS.add(shortLinkDeviceRespDTO);
        });
        //访问网络类型详情
        List<ShortLinkStatsNetworkRespDTO> shortLinkStatsNetworkRespDTOS = new ArrayList<>();
        List<LinkNetworkStatsDO> linkNetworkStatsDOS = linkNetworkStatsMapper.listNetworkStatsByShortLink(requestParam);
        int netWorkCntSum = linkNetworkStatsDOS.stream()
                .mapToInt(LinkNetworkStatsDO::getCnt)
                .sum();
        linkNetworkStatsDOS.forEach(each->{
            double ratio = (double) each.getCnt()/netWorkCntSum;
            double actualRatio = Math.round(ratio*100)/100.0;
            ShortLinkStatsNetworkRespDTO shortLinkStatsNetworkRespDTO = ShortLinkStatsNetworkRespDTO.builder()
                    .cnt(each.getCnt())
                    .network(each.getNetwork())
                    .ratio(actualRatio)
                    .build();
            shortLinkStatsNetworkRespDTOS.add(shortLinkStatsNetworkRespDTO);
        });
        return ShortLinkStatsRespDTO.builder()
                .daily(BeanUtil.copyToList(listStatsByShortLink,ShortLinkStatsAccessDailyRespDTO.class))
                .localeCnStats(localeCNRespDTOS)
                .hourStats(hourStats)
                .topIpStats(topIpStats)
                .weekdayStats(weekdatStats)
                .browserStats(browserRespDTOList)
                .osStats(osRespDTOS)
                .uvTypeStats(shortLinkStatsUvRespDTOS)
                .deviceStats(deviceRespDTOS)
                .networkStats(shortLinkStatsNetworkRespDTOS)
                .build();

    }

    /**
     * 单个短链接访问记录
     * @param requestParam
     * @return
     */
    @Override
    public IPage<ShortLinkAccessRecordRespDto> shortLinkStatsAccessRecord(ShortLinkAccessRecordReqDTO requestParam) {
        LambdaQueryWrapper<LinkAccessLogsDo> queryWrapper = Wrappers.lambdaQuery(LinkAccessLogsDo.class)
                .eq(LinkAccessLogsDo::getGid, requestParam.getGid())
                .eq(LinkAccessLogsDo::getFullShortUrl, requestParam.getFullShortUrl())
                .between(LinkAccessLogsDo::getCreateTime,requestParam.getStartDate(),requestParam.getEndDate())
                .eq(LinkAccessLogsDo::getDelFlag, 0)
                .orderByDesc(LinkAccessLogsDo::getCreateTime);
        IPage<LinkAccessLogsDo> linkAccessLogsDoIPage = linkAccessLogsMapper.selectPage(requestParam, queryWrapper);
        IPage<ShortLinkAccessRecordRespDto> actualResult = linkAccessLogsDoIPage.convert(each ->
                BeanUtil.toBean(each, ShortLinkAccessRecordRespDto.class));
        List<String> userAccessLogsList = actualResult.getRecords()
                .stream()
                .map(ShortLinkAccessRecordRespDto::getUser)
                .toList();
        List<Map<String,Object>> uvTypeList =  linkAccessLogsMapper.selectUvTypeByUser(
                requestParam.getGid(),
                requestParam.getFullShortUrl(),
                requestParam.getStartDate(),
                requestParam.getEndDate(),
                userAccessLogsList);
        actualResult.getRecords().stream().forEach(each->{
           String uvType = uvTypeList.stream()
                            .filter(item -> Objects.equals(each.getUser(), item.get("user")))
                            .findFirst()
                            .map(item -> item.get("user"))
                            .map(Object::toString)
                            .orElse("就访客");
           each.setUvType(uvType);
        }
        );

        return actualResult;
    }


}